//
// Copyright (C) 2004-2010 Autodesk, Inc.
// 
// This library is free software; you can redistribute it and/or
// modify it under the terms of version 2.1 of the GNU Lesser
// General Public License as published by the Free Software Foundation.
// 
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef FDOCMSBOUNDINGBOX_H
#define FDOCMSBOUNDINGBOX_H

#ifdef _WIN32
#pragma once
#endif // _WIN32

#include <vector>
#include <gdal.h>
#include <gdal_priv.h>
#include <FdoCommonThreadMutex.h>

using namespace std;
struct DelegateParams
{
    FdoDouble dCenterLat;
    FdoDouble dCenterLon;
    FdoInt32 iZoomLevel;
    FdoSize height;
    FdoSize width;
    FdoString* imageFormat;
    FdoString* mapType;
    FdoStringP apiKey;
};


class FdoCMSBoundingBox : public FdoXmlSaxHandler, public FdoIDisposable
{
	typedef FdoXmlSaxHandler BaseType;

public:
    static FdoCMSBoundingBox* Create(void);

public:
    static void GetCenter(double mMinX, double mMaxX, double mMinY, double mMaxY, double& latitude, double& longitude);
    static void PixelXYToLatLong(FdoSize pixelX, FdoSize pixelY, int levelOfDetail, double& latitude, double& longitude);
    static void LatLongToPixelXY(double latitude, double longitude, int levelOfDetail, int& pixelX, int& pixelY);
    static int  GetLogoHeight(size_t iX, size_t iY, size_t CountX, size_t CountY);

    static int long2tilex(double lon, int z) ;
    static int lat2tiley(double lat, int z);

    FdoDouble GetMinX() const;
    void SetMinX(FdoDouble value);

    FdoDouble GetMinY() const;
    void SetMinY(FdoDouble value);

    FdoDouble GetMaxX() const;
    void SetMaxX(FdoDouble value);

    FdoDouble GetMaxY() const;
    void SetMaxY(FdoDouble value);

    FdoDouble GetCenterX() const;

    FdoDouble GetCenterY() const;
    void Verify();
    void InitFromXml(FdoXmlSaxContext* context, FdoXmlAttributeCollection* attrs);

    virtual FdoXmlSaxHandler* XmlStartElement(
        FdoXmlSaxContext* context, 
        FdoString* uri, 
        FdoString* name, 
        FdoString* qname, 
        FdoXmlAttributeCollection* atts
    );

    FdoBoolean XmlEndElement(FdoXmlSaxContext* context, FdoString* uri, FdoString* name, FdoString* qname);

protected:
    FdoCMSBoundingBox(void);
    virtual ~FdoCMSBoundingBox(void);
    void Dispose();

private:
    FdoDouble mMinX;
    FdoDouble mMinY;
    FdoDouble mMaxX;
    FdoDouble mMaxY;
};

typedef FdoPtr<FdoCMSBoundingBox> FdoCMSBoundingBoxP;

class FdoCMSImageSpaceTile
{
public:
    FdoCMSImageSpaceTile(double mMinX, double mMaxX, double mMinY, double mMaxY, FdoSize mHeight, FdoSize mWidth
        , FdoSize mPixelStartX, FdoSize mPixelStartY);
    FdoCMSImageSpaceTile(FdoSize offsetX, FdoSize offsetY, FdoSize offsetHeight, FdoSize offsetWidth, FdoSize mHeight, FdoSize mWidth
        , FdoSize mPixelStartX, FdoSize mPixelStartY);
    ~FdoCMSImageSpaceTile();
    // return raster band num
    int SetIoStream(FdoIoStream* pIoStream);
    void SetTileXY(FdoSize tileX, FdoSize tileY);
    FdoSize GetTileX();
    FdoSize GetTileY();

    void SetDelegateParams(DelegateParams* params);
    DelegateParams* GetDelegateParams();

    GDALDataset* GetCMSImageDataset(FdoIoStream* rasterStream);

    FdoIoStream* m_pImageStream;
    FdoCMSBoundingBox* m_boundingBox;
    GDALDataset* m_pGdalDataset;
    FdoSize m_height;
    FdoSize m_width;
    FdoSize m_PixelStartX;
    FdoSize m_PixelStartY;
    char m_vfileNameTile[256];

    DelegateParams* m_params;
    FdoSize m_offsetX;
    FdoSize m_offsetY;
    FdoSize m_offsetHeight;
    FdoSize m_offsetWidth;
    FdoSize m_tileX;
    FdoSize m_tileY;
};

// one line of the image space tile
typedef vector<FdoCMSImageSpaceTile*> FdoCMSImageSpaceLine;

class FdoCMSImageSpaceTileManager
{
public:
    FdoCMSImageSpaceTileManager(FdoSize heightMatched, FdoSize widthMatched);
    ~FdoCMSImageSpaceTileManager();

    FdoSize CountX() const;
    FdoSize CountY() const;

    FdoCMSImageSpaceTile* GetTile(FdoSize indexX, FdoSize indexY);

    void CurrentIndex(FdoSize& indexX, FdoSize& indexY);

    // SetXYCount first, then BeginWrite, AppendTile, EndWrite
    void SetXYCount(FdoSize indexX, FdoSize indexY);
    void BeginWrite();
    void AppendTile(FdoCMSImageSpaceTile* spaceTile, FdoSize& indexX, FdoSize& indexY);
    void EndWrite();

    void BeginRead();
    FdoCMSImageSpaceTile* ReadNext();
    void EndRead();

    int DoQueryYahoo(FdoSize tileCount, FdoString* url, int zoomLevel, FdoDouble ver);
    int DoQueryBing(FdoSize tileCount);
    int DoQueryGoogle(FdoSize tileCount);

    FdoIoStream* MergeImageTiles(int rasterBandNum);
    GDALDataset* GetCMSImageDataset(FdoIoStream* rasterStream);

    GDALDataset* MergeImageTilesGDAL(int rasterBandNum);
    GDALDataset* MergeImageTilesGDALYahoo(int rasterBandNum, FdoSize offsetX, FdoSize offsetY, FdoSize heightMatched, FdoSize widthMatched);
    // validate the tiles.
    // 1 the same image height in the same X 
    // 2 the same image width in the same Y 
    // 3 the same tile num for each row.
    bool validate();
protected:

    void Clear();
    void Reset();
    vector<FdoCMSImageSpaceLine*> m_tilesSquare;
    FdoCMSImageSpaceLine* m_currentLine;
    FdoSize m_currentIndexX;
    FdoSize m_currentIndexY;
    FdoSize m_countX;
    FdoSize m_countY;
    FdoSize m_heightAll;
    FdoSize m_widthAll;
    char m_vfileNameMerged[256];
    GDALDataset* m_GDALDataset;
    FdoCommonThreadMutex m_mutex;
};

#endif // FDOCMSBOUNDINGBOX_H


